/*
 * @Author: wuqingchun
 * @Date: 2024-10-17 11:45:15
 * @Description: 
 * @LastEditors: wuqingchun
 * @LastEditTime: 2024-10-31 20:53:28
 */

#include <cstdio>
#include <thread>
#include <cstring>
#include <cerrno>
#include <getopt.h>
#include <sys/prctl.h>
#include <arpa/inet.h>

#include "libnet.h"
#include "libnet_error.h"
#include "net_tcp_connection.h"
#include "net_event_select.h"
#include <unordered_map>
#include "net_connection_helper.h"
#include "net_server.h"


class CClientCallback : public INetTCPConnectionCallback
{
public:
    virtual void OnConnected(INetTCPConnection *pobjConn)
    {
        printf("%s\n", __FUNCTION__);
    }

    virtual void OnDisconnected(INetTCPConnection *pobjConn)
    {
        printf("%s\n", __FUNCTION__);
    }

    virtual void OnMessage(const void* pData, size_t ulDataLen)
    {
        printf("%s, %lu\n", (const char*)pData, ulDataLen);
        printf("%s\n", __FUNCTION__);
    }
};


class CTCPClient : public INetTCPConnectionCallback
{
public:
    CTCPClient()
    {

    }

    ~CTCPClient()
    {

    }

    int32_t Init(const char* pcHost, uint16_t usPort)
    {
        if (inet_pton(AF_INET, pcHost, &m_stAddr.sin_addr) != 1)
        {
            printf("Invalid parameter\n");
            return -1;
        }

        m_stAddr.sin_family = AF_INET;
        m_stAddr.sin_port = htons(usPort);

        return 0;
    }

    int32_t Start()
    {
        m_bConnMgrRunning = true;
        m_objConnMgrThread = std::thread(&CTCPClient::__ConnMgrThreadFunc, this);
    }

    int32_t AddConnection()
    {
        CNetConnectionActive* pobjConn = CNetConnectionHelper::CreateActive(&m_objSelect, this, &m_stAddr);
        if (pobjConn == nullptr)
        {
            return -1;
        }

        m_mapConnection[pobjConn] = false;

        return 0;
    }

    int32_t Send(const void* pData, size_t ulDataLen)
    {
    }
public:
    virtual void OnConnected(INetTCPConnection* pobjConnection)
    {
        printf("%s\n", __FUNCTION__);
        m_mapConnection[pobjConnection] = true;
    }

    virtual void OnDisconnected(INetTCPConnection *pobjConn)
    {
        printf("%s\n", __FUNCTION__);
    }

    virtual void OnMessage(const void* pData, size_t ulDataLen)
    {
        printf("%s, %lu\n", (const char*)pData, ulDataLen);
        printf("%s\n", __FUNCTION__);
    }
private:
    void __ConnMgrThreadFunc()
    {
        printf("Connection manager thread is running\n");

        prctl(PR_SET_NAME, "conn_mgr");

        while (m_bConnMgrRunning)
        {
            if (m_objSelect.Wait() != ERROR_SUCCESS)
            {
                break;
            }
        }
    }
private:
    CNetEventSelect m_objSelect;
    std::unordered_map<INetTCPConnection*, bool> m_mapConnection;

    std::thread m_objConnMgrThread;
    volatile bool m_bConnMgrRunning = false;

    sockaddr_in m_stAddr{};
};

struct NetOption
{
    std::string strHost;
    uint16_t usPort;
    bool bIsServer;
    uint16_t usRole;
    uint32_t uiInterval;
};

NetOption g_stOption;

static void __main_Select()
{
    CTCPClient objClient;

    int32_t iErr = objClient.Init(g_stOption.strHost.c_str(), g_stOption.usPort);
    if (iErr != 0)
    {
        return ;
    }

    for (int i = 0; i < g_stOption.uiInterval; i++)
    {
        objClient.AddConnection();
    }

    objClient.Start();

    while (true)
    {
        objClient.Send("Hello", sizeof("Hello"));

        std::this_thread::sleep_for(std::chrono::seconds(g_stOption.uiInterval));
    }
}

static void __main_RunAsClient()
{
    
    CClientCallback objCB;
    INetTCPClient* pobjClient = NewTCPClient(&objCB, g_stOption.strHost.c_str(), g_stOption.usPort);
    if (pobjClient == nullptr)
    {
        return;
    }

    pobjClient->Start();

    int32_t iErr;

    while (true)
    {
        iErr = pobjClient->SendMessage("PING", sizeof("PING"));
        if (iErr != ERROR_SUCCESS)
        {
            printf("send error, err=%d\n", iErr);
            std::this_thread::sleep_for(std::chrono::seconds(2));
        }

        if (g_stOption.uiInterval > 0)
        {
            std::this_thread::sleep_for(std::chrono::milliseconds(g_stOption.uiInterval));
        }
    }

    pobjClient->Stop();

    DeleteTCPClient(pobjClient);
}


static void __main_RunAsServer()
{
    CNetServer objServer;

    objServer.Init(g_stOption.strHost.c_str(), g_stOption.usPort);

    objServer.Start();

    while (true)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(g_stOption.uiInterval));
    }

}

static void __main_ParseOption(int argc, char** argv, NetOption& refOption)
{
    static struct option astLOpts[] = {
        {"help",       no_argument,       0, 'h'},
        {"listen",     no_argument,       0, 'l'},
        {"interval",   required_argument, 0, 'i'},
        {0,            0,                 0,  0}
    };

    while (true)
    {
        int this_option_optind = optind ? optind : 1;
        int option_index = 0;

        int c = getopt_long(argc, argv, "hli:", astLOpts, &option_index);
        if (c == -1)
        {
            break;
        }

        switch (c)
        {
        case 'l':
        {
            refOption.bIsServer = true;
            break;
        }
        case 'i':
        {
            refOption.uiInterval = atoi(optarg);
            break;
        }
        case 'h':
        case '?':
        default:
            exit(EXIT_FAILURE);
            break;
        }
    }

    if (optind + 2 > argc)
    {
        printf("Unknown address and port\n");
        exit(EXIT_FAILURE);
    }

    refOption.strHost = argv[optind];
    refOption.usPort = atoi(argv[optind + 1]);
}

int main(int argc, char** argv)
{
    __main_ParseOption(argc, argv, g_stOption);

    if (g_stOption.bIsServer)
    {
        __main_RunAsServer();

        return 0;
    }

    __main_Select();

    return 0;
}